﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Text;
using System.Security.Cryptography;
using System.Reflection;
using System.Xml;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;
using log4net;

namespace UPNP.IO
{
	public static class DeviceHelpers
	{
        private static readonly ILog _log = LogManager.GetLogger(typeof(DeviceHelpers));
		private readonly static SHA1CryptoServiceProvider _sha1 = new SHA1CryptoServiceProvider();

		/// <summary>
		/// calculates an id from unique data
		/// 
		/// same data will always result in the same id.
		/// different data will always result in different ids.
		/// </summary>
		public static string CreateId(params string[] data)
		{
			var data2 = string.Join("\n", data);

			var sha1 = _sha1.ComputeHash(Encoding.UTF8.GetBytes(data2));

			return BitConverter.ToString(sha1).Replace("-", string.Empty).ToLower();
		}

        public const string EnumInvalidValueName = "_UNSUPPORTED_";

        public static string EnumValueToMemberName(string val)
		{
            if (string.IsNullOrEmpty(val))
                return EnumInvalidValueName;

			// drop invalid characters
			val = val.Replace(' ', '_').Replace('.', '_').Replace('-', '_').Trim();

			// handle the case that the first char is numeric
			if (char.IsNumber(val[0]))
				val = ("N" + val);
			
			return val;
		}

        public static T_Enum ParseEnumValue<T_Enum>(string val)
        {
            if (string.IsNullOrEmpty(val))
            {
                return (T_Enum)Enum.Parse(typeof(T_Enum), EnumInvalidValueName);
            }

            // try parsing the value
            try
            {
                return (T_Enum)Enum.Parse(typeof(T_Enum), val, true);
            }
            catch (Exception x)
            {
                _log.Warn("Failed to parse value for enum type " + typeof(T_Enum).FullName + ": " + val, x);

                return (T_Enum)Enum.Parse(typeof(T_Enum), EnumInvalidValueName);
            }
        }

        /// <summary>
        /// source: http://www.codeproject.com/KB/recipes/hexencoding.aspx?msg=2494780#xx2494780xx
        /// </summary>
        public static byte[] HexToData(string hexString)
        {
            if (hexString == null)
                return null;

            if (hexString.Length % 2 == 1)
                hexString = '0' + hexString; // Up to you whether to pad the first or last byte

            byte[] data = new byte[hexString.Length / 2];

            for (int i = 0; i < data.Length; i++)
                data[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);

            return data;
        }

		private static readonly Regex TypeIdPattern = new Regex(@"([a-z0-9:-]+):([A-Za-z0-9]+):([0-9]+)");

		/// <summary>
		/// urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1 --> WANCommonInterfaceConfig1
		/// urn:schemas-upnp-org:device:InternetGatewayDevice:1 --> InternetGatewayDevice1
		/// </summary>
		public static string TypeIdToName(string typeId)
		{
			if (string.IsNullOrEmpty(typeId))
				throw new ArgumentException("null or empty", "typeId");

			// match regex
			var m = TypeIdPattern.Match(UnifyTypeId(typeId));

			if (!m.Success)
				throw new ArgumentException("not a device or service type id", "typeId");

			// return the member name
			return (m.Groups[2].Value + m.Groups[3].Value);
		}

        private static readonly Regex FullNamespaceCamlCasePattern = new Regex(@"^[A-Za-z]|[\.:-][A-Za-z]");
        private static readonly Regex FullNamespaceDeviceServicePattern = new Regex(
            @"^urn\.|^device\.|^service\.|^schemas?-|(?<=\.)device\.|(?<=\.)service\.|(?<=\.)schemas?-|\.device$|\.service$|\.schemas?-$|\.$");

        /// <summary>
        /// urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1 --> schemas-upnp-org.WANCommonInterfaceConfig1
        /// urn:schemas-upnp-org:device:InternetGatewayDevice:1 --> schemas-upnp-org.InternetGatewayDevice1
        /// </summary>
        public static string TypeIdToFullNamespace(string typeId)
        {
            if (string.IsNullOrEmpty(typeId))
                throw new ArgumentException("null or empty", "typeId");

            // match regex
            var m = TypeIdPattern.Match(UnifyTypeId(typeId));

            if (!m.Success)
                throw new ArgumentException("not a device or service type id", "typeId");

            // create prefix
            var prefix = m.Groups[1].Value;

            prefix = prefix.Replace(':', '.');                  // convert to c# namespace separators

            prefix = FullNamespaceDeviceServicePattern.Replace(// remove device/service distinction
                prefix,
                (mm) => string.Empty
                );

            prefix = FullNamespaceCamlCasePattern.Replace(      // ensure camel case
                prefix,
                (mm) => mm.Value.ToUpper()
                );

            prefix = prefix.Replace("-", string.Empty);         // strip illegal chars

            // return the member name
            return (prefix + "." + m.Groups[2].Value + m.Groups[3].Value);
        }

		public static string UnifyTypeId(string typeId)
		{
			if (string.IsNullOrEmpty(typeId))
				throw new ArgumentException("null or empty", "typeId");

			return typeId.Replace(" ", "").Trim();
		}
	}
}
